package com.samp.solr.hanlp.tokenize;

import com.hankcs.hanlp.HanLP;
import com.hankcs.hanlp.dictionary.CustomDictionary;
import com.hankcs.hanlp.model.crf.CRFLexicalAnalyzer;
import com.hankcs.hanlp.seg.CRF.CRFSegment;
import com.hankcs.hanlp.seg.NShort.NShortSegment;
import com.hankcs.hanlp.seg.Segment;
import com.hankcs.hanlp.seg.Viterbi.ViterbiSegment;
import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.tokenizer.IndexTokenizer;
import com.hankcs.hanlp.tokenizer.NLPTokenizer;
import com.hankcs.hanlp.tokenizer.SpeedTokenizer;

import java.util.ArrayList;
import java.util.List;

/**
 * 参考：https://github.com/hankcs/HanLP#6-crf%E5%88%86%E8%AF%8D
 * 常用的分词器：StandardTokenizer，nlpTokenizer，indexTokenizer，speedTokenizer。
 * 各有用途，标准分词器最常用，各方面最平衡。nlp速度慢，但对词性标注更准确，speed速度最快，精度一般，index可能进行细分。
 * 另外，还有针对繁体字的：TraditionalChineseTokenizer
 * 以上分词器都可以通过静态方法直接调用。
 * 其它的NShortSegment，ViterbiSegment使用场景较少。CRF新词识别能力强。
 * 一般还是通过Segment segment = HanLP.newSegment();来获取实例，这个同时可以设置属性。
 *
 * 注：分词器都是线程安全的。
 */
public class TokenizeDemo {

    /**
     * 默认分词器,也就是把标准分词封闭了下：StandardTokenizer。
     * 只开启了中国人名识别和音译人名识别
     */
    private static void defaultTokenize(List<String> list){
        long start = System.currentTimeMillis();
        System.out.println("=============默认(标准)分词器=============");
        for( String str: list ) {
            List<Term> termList = HanLP.segment(str);
            System.out.println(termList);
        }
        System.out.println("cost:"+(System.currentTimeMillis()-start));
    }

    /**
     * NLPTokenizer 会执行全部命名实体识别和词性标注,所以速度比标准分词慢，并且有误识别的情况
     * @param list
     */
    private static void nlpTokenizer (List<String> list){
        long start = System.currentTimeMillis();
        System.out.println("=============NLP分词器=============");
        for( String str: list ) {
            List<Term> termList = NLPTokenizer.segment(str);
            System.out.println(termList);
        }
        System.out.println("cost:"+(System.currentTimeMillis()-start));
    }

    /**
     * 索引分词是面向搜索引擎的分词器，能够对长词进行全切分，另外通过 term.offset 可以获取单词在文本中的偏移量
     * @param list
     */
    private static void indexTokenizer (List<String> list){
        long start = System.currentTimeMillis();
        System.out.println("=============index分词器=============");
        for( String str: list ) {
            List<Term> termList = IndexTokenizer.segment(str);
            System.out.println(termList);
//            for( Term term :termList ){
//                System.out.println(term + " [" + term.offset + ":" + (term.offset + term.word.length()) + "]");
//            }
        }
        System.out.println("cost:"+(System.currentTimeMillis()-start));
    }

    /**
     * 极速分词也就是词典最长分词，速度极快，不会返回词性，精度一般。
     * @param list
     */
    private static void speedTokenizer (List<String> list){
        long start = System.currentTimeMillis();
        System.out.println("=============speed分词器=============");
        for( String str: list ) {
            List<Term> termList = SpeedTokenizer.segment(str);
            System.out.println(termList);
        }
        System.out.println("cost:"+(System.currentTimeMillis()-start));
    }

    private static void nShortSegment (List<String> list){
        long start = System.currentTimeMillis();
        System.out.println("=============nShortSegment分词器=============");
        Segment nShortSegment = new NShortSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);
        for( String str: list ) {
            List<Term> termList = nShortSegment.seg(str);
            System.out.println(termList);
        }
        System.out.println("cost:"+(System.currentTimeMillis()-start));
    }

    private static void shortestSegment (List<String> list){
        long start = System.currentTimeMillis();
        System.out.println("=============shortestSegment分词器=============");
        Segment shortestSegment = new ViterbiSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);
        for( String str: list ) {
            List<Term> termList = shortestSegment.seg(str);
            System.out.println(termList);
        }
        System.out.println("cost:"+(System.currentTimeMillis()-start));
    }

    /**
     * CRF对新词有很好的识别能力，但开销较大。
     * @param list
     */
    private static void crfSegment (List<String> list){
        System.out.println("=============crfSegment分词器=============");
        try {
            CRFLexicalAnalyzer analyzer = new CRFLexicalAnalyzer();
            long start = System.currentTimeMillis();
            for (String str : list) {
                List<Term> termList = analyzer.seg(str);
                System.out.println(termList);
            }
            System.out.println("cost:"+(System.currentTimeMillis()-start));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private static void addCustomWords(){
        CustomDictionary.add("代发薪");
        CustomDictionary.add("越来越多");
    }

//    /**
//     * 分词器有各种功能定制，可根据需要开启.
//     */
//    private static void setupSegment(){
//        //通过HanLP.newSegment()获取当前版本速度和效果最平衡的分词器。
//        Segment segment = HanLP.newSegment();
//        segment.enableIndexMode(true);//设为索引模式
//        segment.enableCustomDictionary(true);//开启自定义词典，还有命名识别等
//
//    }

    public static void main(String[] args) {
        addCustomWords();
        List<String> list = new ArrayList<>();
//        list.add("商品和服务");
//        list.add("我新造一个词叫幻想乡你能识别并标注正确词性吗？");
//        list.add("我的希望是希望张晚霞的背影被晚霞映红");
//        list.add("去年老张中秋去“泡茶”，送礼遭到了拒绝，老张担心金额不够。");
//        list.add("已结婚的和尚未结婚的都要来登记。");
//        list.add("中国科学院计算技术研究所的宗成庆教授正在教授自然语言处理课程。");
//        list.add("刘喜杰石国祥会见吴亚琴先进事迹报告团成员。");
//        list.add("2018年代发薪工作总结报告");
//        list.add("代发薪介绍");
//        list.add("越来越多的攻城狮喜欢用派深，慢慢放弃了爪哇");
        list.add("我要找我的那个工号三12370帮我办理保险那个");
        list.add("我想联系一下洪绘，那个工号是要要1118450的");
        list.add("我说要我是我想如果如果卡你看我的保单银行还在吗");
        list.add("美国贸易代表莱特希泽（Robert Lighthizer）和财政部长姆努钦（Steven Mnuchin）前天抵达北京，与中国副总理、中美全面经济对话中方牵头人刘鹤共进工作晚餐，并于昨天举行正式磋商。");
        list.add("分析：续争谈判之余 中国不应示弱");
        list.add("摩拜单车要涨价了，下周一起骑半小时要多付0.5元");
        list.add("不过成晓河认为，特朗普玩的更多是“边缘策略”（brinkmanship），为争取最大好处而铤而走险。谈判破局的风险虽然增加了，但并未达到破裂或无可挽回的地步。");
        defaultTokenize(list);
        nlpTokenizer(list);
        indexTokenizer(list);
        speedTokenizer(list);
        System.out.println("*********************************************************");
        nShortSegment(list);
        shortestSegment(list);
        crfSegment(list);
    }

}
